package org.zhongyin.Dao;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;

import jxl.read.biff.BiffException;

import org.joda.time.DateTime;
import org.zhongyin.JPA.Entity.OBJECTS;
import org.zhongyin.JPA.Entity.PermanentHistoryData;
import org.zhongyin.Util.Utility;
import org.zhongyin.internal.core.ARXFirstWDProvider;
import org.zhongyin.internal.core.ARXFirstWEProvider;

/**
 * @author bf_zzhang
 * 
 */
public class ARCIVE_DBDaoImpl implements IARCHIVE_DBDao {

	private static final String PERSISTENCE_UNIT_NAME = "ARCHIVE_DB";
	private static EntityManagerFactory factory;
	private EntityManager em;

	public ARCIVE_DBDaoImpl() {
		factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
		em = factory.createEntityManager();
	}

	/**
	 * Retrieve data before 2012-06-28
	 */
	@Override
	public List<Object[]> getHistData() {
		Query query = em.createNativeQuery("SELECT NHWD.NOAA_DATE_COLLECTED, "
				+ "							DAYOFWEEK(DWS.DATE)-1 AS dayofweek, "
				+ "							HOUR(NHWD.NOAA_DATE_COLLECTED) AS hour, "
				+ "							DWS.MAX_TEMPERATURE AS max_t, "
				+ "							DWS.MIN_TEMPERATURE AS  min_t, "
				+ "							NHWD.DRY_BULB_F AS T_t " + "FROM "
				+ "NOAA_HOURLY_WEATHER_DATA AS NHWD " + "INNER JOIN "
				+ "DAILY_WEATHER_SUMMARY AS DWS " + "ON "
				+ "CAST(NHWD.NOAA_DATE_COLLECTED AS DATE) = DWS.DATE " +

				"WHERE " +

				"DWS.DATE > '2010-01-01' " +

				"ORDER BY NHWD.NOAA_DATE_COLLECTED;");
		List<Object[]> result = query.getResultList();
		return result;
	}

	/**
	 * Retrieve Cooling Load Data after 2012-06-30
	 */
	@Override
	public List<PermanentHistoryData> getHistCLData() {
		GREEDODaoImpl greedo = new GREEDODaoImpl();
		OBJECTS pk = greedo.getPK();

		int objIdTop = pk.getId().getObjIdTop();
		int objIdMiddle = pk.getId().getObjIdMiddle();
		int objIdBottom = pk.getId().getObjIdBottom();

		Query query = em.createQuery("SELECT t FROM PermanentHistoryData t "
				+ "WHERE " + "t.id.objIdTop = :objIdTop AND "
				+ "t.id.objIdMiddle = :objIdMiddle AND "
				+ "t.id.objIdBottom = :objIdBottom AND "
				+ "t.id.histDateTime >= '2012-09-01 23:00:00' " + "ORDER BY "
				+ "t.id.histDateTime");

		query.setParameter("objIdTop", objIdTop);
		query.setParameter("objIdMiddle", objIdMiddle);
		query.setParameter("objIdBottom", objIdBottom);

		List<PermanentHistoryData> result = query.getResultList();

		// Utility.printArrayListCL(result);

		return result;
	}

	/**
	 * Retrieve Data after 2012-06-30
	 * 
	 * @throws ParseException
	 */
	@Override
	public List<Object[]> getRecentData() throws ParseException {
		Query query = em.createNativeQuery("SELECT HW.DATE_COLLECTED, "
				+
				// "DAYOFWEEK(DWS.DATE)-1 AS dayofweek, " +
				// "HOUR(HW.DATE_COLLECTED) AS hour, " +
				"DWS.MAX_TEMPERATURE AS max_t, "
				+ "DWS.MIN_TEMPERATURE AS  min_t, " + "HW.DRY_BULB_F AS T_t, "
				+ "HW.RELATIVE_HUMIDITY_PCT AS RH_t " + "FROM "
				+ "HOURLY_WEATHER AS HW " + "INNER JOIN "
				+ "DAILY_WEATHER_SUMMARY AS DWS " + "ON "
				+ "CAST(HW.DATE_COLLECTED AS DATE) = DWS.DATE " + "WHERE "
				+ "HW.DATE_COLLECTED >= '2012-09-01 23:00:00' AND "
				+ "HW.DATE_COLLECTED < CURRENT_DATE() - INTERVAL 10 MINUTE "
				+ "ORDER BY HW.DATE_COLLECTED ");

		List<Object[]> result = query.getResultList();

		result = dataProcess(result);
		
		List<Object[]> newResult = new ArrayList<Object[]>();

		Set<Date> filter = new HashSet<Date>();

		result = dateConvert(result);

		for (Object[] element : result) {
			if (!filter.contains(element[0])) {
				filter.add((Date) element[0]);
				newResult.add(element);
			}
		}

		Collections.sort(newResult, new SortByDate());
		return newResult;
	}

	/**
	 * Get the prediction data after current hour
	 * 
	 * @return
	 * @throws ParseException
	 */
	public List<Object[]> getForecastTodayData() throws ParseException {
		Query query = em
				.createNativeQuery("SELECT FORECAST_TARGET, TEMPERATURE_F AS T_t, REL_HUMIDITY_PCT AS RH_t FROM PAST_FORECASTS "
						+ "							WHERE Hour(FORECAST_TARGET) > Hour(NOW()) AND "
						+ "							date(FORECAST_TARGET) = CURRENT_DATE AND date(FORECAST_OBTAINED) = CURRENT_DATE "
						+ "							ORDER BY FORECAST_OBTAINED DESC");
		List<Object[]> result = query.getResultList();

		List<Object[]> newResult = new ArrayList<Object[]>();

		Set<Date> filter = new HashSet<Date>();

		result = dateConvert(result);

		for (Object[] element : result) {
			if (!filter.contains(element[0])) {
				filter.add((Date) element[0]);
				newResult.add(element);
			}
		}

		Collections.sort(newResult, new SortByDate());
//		Utility.printArrayList(newResult);
		return newResult;
	}

	class SortByDate implements Comparator<Object[]> {

		@Override
		public int compare(Object[] o1, Object[] o2) {
			return ((Date) o1[0]).compareTo((Date) o2[0]);
		}
	}
	
	/**
	 * get next 24 hours temperature and Relative humidity
	 * @return
	 * @throws ParseException
	 */
	
	public List<Object[]> getForcastNext24Hours() throws ParseException{
		Query query = em
				.createNativeQuery("SELECT FORECAST_TARGET, TEMPERATURE_F AS T_t, REL_HUMIDITY_PCT AS RH_t FROM PAST_FORECASTS "
						+ "							WHERE FORECAST_TARGET> NOW() AND FORECAST_TARGET < NOW() + INTERVAL 24 HOUR AND " +
						"							date(FORECAST_OBTAINED) = CURRENT_DATE "
						+ "							ORDER BY FORECAST_OBTAINED DESC");
		List<Object[]> result = query.getResultList();

		List<Object[]> newResult = new ArrayList<Object[]>();

		Set<Date> filter = new HashSet<Date>();

		result = dateConvert(result);

		for (Object[] element : result) {
			if (!filter.contains(element[0])) {
				filter.add((Date) element[0]);
				newResult.add(element);
			}
		}
		
//		System.out.println(newResult.size());

		Collections.sort(newResult, new SortByDate());
//		Utility.printArrayList(newResult);
		
		
		return newResult;
		
		
	}

	/**
	 * Get todays data between last hour and small hours
	 * 
	 * @return
	 * @throws ParseException
	 */

	public List<Object[]> getTodaysEarlyData() throws ParseException {
		Query query = em
				.createNativeQuery("SELECT DATE_COLLECTED, DRY_BULB_F AS T_t, RELATIVE_HUMIDITY_PCT AS RH_t FROM HOURLY_WEATHER "
						+ "							WHERE DATE_COLLECTED > CURRENT_DATE()-  INTERVAL 10 MINUTE ");

		List<Object[]> result = query.getResultList();
		result = dateConvert(result);
		// Utility.printArrayList(result);

		return result;
	}

	public List<Object[]> getCurrentHourData() throws ParseException {
		Query query = em
				.createNativeQuery("SELECT FORECAST_TARGET, TEMPERATURE_F AS T_t, REL_HUMIDITY_PCT AS RH_t FROM PAST_FORECASTS "
						+ "							WHERE Hour(FORECAST_TARGET) = Hour(NOW()) AND "
						+ "							date(FORECAST_OBTAINED) = CURRENT_DATE AND DATE(FORECAST_TARGET) = CURRENT_DATE "
						+ "							ORDER BY FORECAST_OBTAINED DESC LIMIT 0 , 1");

		List<Object[]> result = query.getResultList();

		result = dateConvert(result);
		// Utility.printArrayList(result);

		return result;
	}

	/**
	 * Process the retrieved data from database to a regular data format
	 * 
	 * @param data
	 * @return
	 * @throws ParseException
	 */
	@SuppressWarnings("deprecation")
	public List<Object[]> dataProcess(List<Object[]> data)
			throws ParseException {
		// long previousHour = -1L;
		// Set<Integer> removedIndex = new HashSet<Integer>();

		for (Object[] row : data) {
			Date jdkdate = (Date) row[0];

			int minute = jdkdate.getMinutes();

			if (minute >= 30) {

				
				jdkdate.setHours(jdkdate.getHours() + 1);

				row[0] = Utility.converToDateHH(jdkdate);

			} else {
				row[0] = Utility.converToDateHH(jdkdate);
			}

			if (jdkdate.getHours() == 0 && data.indexOf(row) + 1 < data.size()) {
				int index = data.indexOf(row) + 1;
				row[1] = data.get(index)[1];
				row[2] = data.get(index)[2];
			}

		}

		return data;
	}

	public List<Object[]> dateConvert(List<Object[]> data)
			throws ParseException {
		for (Object[] row : data) {
			Date jdkdate = (Date) row[0];

			int minute = jdkdate.getMinutes();

			if (minute >= 30) {
				jdkdate.setHours(jdkdate.getHours() + 1);
				row[0] = Utility.converToDateHH(jdkdate);
			} else {

				row[0] = Utility.converToDateHH(jdkdate);
			}
		}

		return data;
	}

	public List<Object[]> getTodayData() throws ParseException {
		ARCIVE_DBDaoImpl dao = new ARCIVE_DBDaoImpl();

		List<Object[]> today = new ArrayList<Object[]>();

		today.addAll(dao.getTodaysEarlyData());
		today.addAll(dao.getCurrentHourData());
		today.addAll(dao.getForecastTodayData());
		return today;
	}

	public float getMax_t(List<Object[]> today) throws ParseException {
		// List<Object[]> today = getTodayData();
		float max = Utility.getMax(today);
		return max;
	}

	public float getMin_t(List<Object[]> today) throws ParseException {
		// List<Object[]> today = getTodayData();
		float min = Utility.getMin(today);
		return min;
	}

	public List<Object[]> combineHistData(List<Object[]> today,
			List<Object[]> RecentData, List<PermanentHistoryData> CLData)
			throws ParseException {
		float max = getMax_t(today);
		float min = getMin_t(today);

		List<Object[]> combinedata = new ArrayList<Object[]>();

		// Recent data
		for (Object[] element : RecentData) {
			Object[] array = new Object[8];
			array[0] = element[0];
			Date date = (Date) element[0];
			// day of week
			array[1] = date.getDay();
			// hour of day
			array[2] = date.getHours();
			// Max_t
			array[3] = element[1];
			// Min_t
			array[4] = element[2];
			// T_t
			array[5] = element[3];
			// RH_t
			array[6] = element[4];
			combinedata.add(array);
		}

		// today data
		for (Object[] element : today) {
			Object[] array = new Object[8];
			Date date = (Date) element[0];
			Date now = new Date();

			if (date.getHours() <= now.getHours()) {
				array[0] = element[0];

				// day of week
				array[1] = date.getDay();
				// hour of day
				array[2] = date.getHours();
				// Max_t
				array[3] = max;
				// Min_t
				array[4] = min;
				// T_t
				array[5] = element[1];
				// RH_t
				array[6] = element[2];
				combinedata.add(array);
			}
		}

		Map<Date, Float> clMap = new HashMap<Date, Float>();
		for (PermanentHistoryData element : CLData) {
			clMap.put(element.getId().getHistDateTime(), element.getHistValue());
		}

		for (Object[] element : combinedata) {
			element[7] = clMap.get(element[0]);
		}

//		Utility.printArrayList(combinedata);

		return combinedata;
	}
	
	
	public List<Object[]> combineNext24HoursData(List<Object[]> next24Hours, List<Object[]> recentData) throws ParseException{
		float max = getMax_t(next24Hours);
		float min = getMin_t(next24Hours);
		
		List<Object[]> combinedata = new ArrayList<Object[]>();
		
		// next 24 hours data
		for (Object[] element : next24Hours) {
			Object[] array = new Object[8];
			Date date = (Date) element[0];
			Date now = new Date();

				array[0] = element[0];

				// day of week
				array[1] = date.getDay();
				// hour of day
				array[2] = date.getHours();
				// Max_t
				array[3] = max;
				// Min_t
				array[4] = min;
				// T_t
				array[5] = element[1];
				// RH_t
				array[6] = element[2];
				
				//initial forcast cooling load value to -1
				array[7] = -1f;
				combinedata.add(array);
			
		}
		
		int counter = 0;
		for(int i = recentData.size()-1; i >= 0; i--){
//			if((int)recentData.get(i)[1] >=1 && (int)recentData.get(i)[1] <=5){
				combinedata.add(recentData.get(i));
				counter++;
				if(counter == 168){
					break;
				}
//			}
		}
		
		Collections.sort(combinedata, new SortByDate());
		
		return combinedata;
		
	}
	

	public static void main(String[] args) throws ParseException, BiffException, IOException {
		ARCIVE_DBDaoImpl dao = new ARCIVE_DBDaoImpl();

		 List<Object[]> rData =dao.getRecentData();
		 List<Object[]> today =dao.getTodayData();
		 List<PermanentHistoryData> clData = dao.getHistCLData();
		 List<Object[]> combine = dao.combineHistData(today, rData, clData);
		 
		 Utility.printArrayList(combine);

		 System.out.println("-------------------------------------------");

		
		 List<Object[]> next24hours = dao.getForcastNext24Hours();
		 
		 Utility.printArrayList(next24hours);
		 
		 System.out.println("-------------------------------------------");
		 List<Object[]> combine24 = dao.combineNext24HoursData(next24hours, combine);
		 
		 Utility.printArrayList(combine24);
		 
		 
		 DateTime now = new DateTime();
		 ARXFirstWEProvider forcastWE = new ARXFirstWEProvider();
		 forcastWE.setForcastData();
		 
		 ARXFirstWDProvider forcastWD = new ARXFirstWDProvider();
		 forcastWD.setForcastData();
		//forcast next 6 hours
		for(int i = 1; i < 7 ; i++){
			DateTime nexthours = now.plusHours(i);
			 if(nexthours.getDayOfWeek() ==6 || nexthours.getDayOfWeek() == 7){
				
				 forcastWE.setARXForcastmodel(i);
				 double[][] forcast_x = forcastWE.getARXmodel_X();
				 Utility.printArray(forcast_x, i-1, i);
			 }else if(nexthours.getDayOfWeek()>=1 && nexthours.getDayOfWeek() <=5){
				 
				 forcastWD.setARXForcastmodel(i);
				 double[][] forcast_x = forcastWD.getARXmodel_X();
				 Utility.printArray(forcast_x, i-1, i);
			 }
		}

//		Date date = new Date();
//		Utility.converToDateHH(date);

	}

}
